package com.ztjava.core.tool;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.config.Plugins;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.dialect.AnsiSqlDialect;
import com.jfinal.plugin.c3p0.C3p0Plugin;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.jfinal.plugin.redis.RedisPlugin;
import com.xiaoleilu.hutool.io.resource.ResourceUtil;
import com.ztjava.core.Constant;
import com.ztjava.core.Jsub;
import com.ztjava.core.MyRedisCache;
import com.ztjava.core.bean.ColumnBean;
import com.ztjava.core.bean.Database;
import com.ztjava.core.bean.TableBean;
import com.ztjava.core.utiliy.PathUtil;
import com.ztjava.plugin.event.EventPlugin;
import com.ztjava.plugin.weixin.WeixinPlugin;

public class ConfigTool {
	private static Logger logger = Logger.getLogger(ConfigTool.class);
	// 配置字段
	private static final String c_db = "db";
	private static final String c_table = "table";
	private static final String c_redis = "redis";
	private static final String c_weixin = "weixin";
	private static final String c_param = "param";
	private static final String c_basedata = "basedata";
	private static final String c_auth = "auth";
	private static final String c_template = "template";
	private static final String c_event = "event";
	private static final String c_export = "export";

	private static final String mysql = "mysql";
	private static final String jndi = "jndi";

	/***
	 * 加载自定义模板
	 * 
	 * @param app_config
	 */
	public void parseTemplate(JSONObject app_config) {
		if (app_config == null || app_config.get(c_template) == null)
			return;
		JSONObject templateObject = app_config.getJSONObject(c_template);
		Cache cache = Redis.use();
		for (String code : templateObject.keySet()) {
			cache.hset(Constant.R_TEMPLATE, code,templateObject.getString(code));
			logger.info("自定义模板：" + code);
		}
	}
	/***
	 * 加载自定义带出模板
	 */
	public void parseExport(JSONObject app_config){
		if (app_config == null || app_config.get(c_export) == null)
			return;
		JSONObject exportObject = app_config.getJSONObject(c_export);
		Cache cache = Redis.use();
		for (String code : exportObject.keySet()) {
			cache.hset(Constant.R_EXPORT, code,exportObject.getJSONObject(code));
			logger.info("导出模板：" + code);
		}
	}

	/***
	 * 常规配置加载
	 * 
	 * @param config
	 */
	public void parseCommon(JSONObject config) {
		logger.info("==========检查常规缓存开始！==========");
		parseParam(config);
		parseBasedata(config);
		parseAuth(config);
		parseTemplate(config);
		parseExport(config);
		logger.info("==========检查常规缓存结束！==========");
	}

	/***
	 * 加载全部配置
	 * 
	 * @param app_config
	 * @param server_config
	 */
	public void parseAllConfig(JSONObject app_config, JSONObject server_config) {
		
		ScanTool scanTool = new ScanTool();
		List<JSONObject> jsonObjects = scanTool.scanSubConfig(app_config);
		if(Jsub.single_app){
			Cache cache = Redis.use();
			cache.del(Constant.R_AUTH);
		}
		
		for (JSONObject item : jsonObjects) {
			parseCommon(item);
		}
		parseCommon(JSONObject.parseObject(ResourceUtil.readUtf8Str("com/ztjava/core/config.json")));
		parseCommon(app_config);
		parseCommon(server_config);

	}

	/***
	 * 加载参数
	 * 
	 * @param config
	 */
	public void parseParam(JSONObject config) {
		if (config == null || config.get(c_param) == null)
			return;
		JSONObject paramObject = config.getJSONObject(c_param);
		Cache cache = Redis.use();
		for (String code : paramObject.keySet()) {
			cache.hset(Constant.R_PARAM, code, paramObject.getString(code));
			logger.info(code + "：" + paramObject.getString(code));
		}
	}

	/****
	 * 加载字典
	 */
	public void parseBasedata(JSONObject config) {
		if (config == null || config.get(c_basedata) == null)
			return;
		JSONObject basedataObject = config.getJSONObject(c_basedata);
		Cache cache = Redis.use();
		for (String code : basedataObject.keySet()) {
			cache.hset(Constant.R_BASEDATA, code,basedataObject.getJSONObject(code));
			logger.info("系统数据字典：" + code);
		}
	}

	/***
	 * 获取基础数据名称对应编码
	 * 
	 * @param code
	 * @param name
	 * @return
	 */
	public static String basedataNameToValue(String code, String name) {
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if (basedata == null)
			return null;
		for (String key : basedata.keySet()) {
			if (basedata.getJSONObject(key).getString("name").equals(name)) {
				return key;
			}
		}
		return "";
	}
	public static JSONObject basedataNameToJSONObject(String code, String name) {
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if (basedata == null)
			return null;
		for (String key : basedata.keySet()) {
			if (basedata.getJSONObject(key).getString("name").equals(name)) {
				return basedata.getJSONObject(key);
			}
		}
		return null;
	}
	public static String basedataNameToValues(String code, String names) {
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if (basedata == null)
			return null;
		if(names==null||names.equals("")){
			return "";
		}
		String[] nameArr = names.split(",");
		String values = "";
		for(String name:nameArr){
			for (String key : basedata.keySet()) {
				if (basedata.getJSONObject(key).getString("name").equals(name)) {
					values = values + key+",";
				}
			}
		}
		if(!values.equals("")){
			values = ","+values;
		}
		return values;
	}
	public static String basedataNameToValues2(String code, String names) {
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if (basedata == null)
			return null;
		if(names==null||names.equals("")){
			return "";
		}
		String[] nameArr = names.split(",");
		String values = "";
		for(String name:nameArr){
			for (String key : basedata.keySet()) {
				if (basedata.getJSONObject(key).getString("name").equals(name)) {
					values = values + key+",";
				}
			}
		}
		if(!values.equals("")){
			values = values.substring(0,values.length()-1);
		}
		return values;
	}
	/***
	 * 检查database的value是否存在
	 * @param code
	 * @param value
	 * @return
	 */
	public static boolean checkBasedataValue(String code,String value){
		Cache cache = Redis.use();
		if(!cache.exists(Constant.R_BASEDATA)){
			return false;
		}
		if(!cache.hexists(Constant.R_BASEDATA, code)){
			return false;
		}
		JSONObject json = cache.hget(Constant.R_BASEDATA, code);
		
		if(json.get(value)==null){
			return false;
		}
		return true;
	}
	public static boolean checkBasedata(String code){
		Cache cache = Redis.use();
		if(!cache.exists(Constant.R_BASEDATA)){
			return false;
		}
		if(!cache.hexists(Constant.R_BASEDATA, code)){
			return false;
		}
		return true;
	}
	public static boolean checkCacheValue(String code,String value){
		Cache cache = Redis.use();
		if(!cache.exists(code)){
			return false;
		}
		if(!cache.hexists(code, value)){
			return false;
		}
		return true;
	}

	/***
	 * 检查编码是否在字典里
	 * 
	 * @param code
	 * @param value
	 * @return
	 */
	public static boolean checkCodeInBasedata(String code, String value) {
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if (basedata == null || basedata.getString(value) == null)
			return false;
		return true;
	}

	/***
	 * 业务表数据缓存
	 * 
	 * @param code
	 * @param tablename
	 * @param orderby
	 */
	public static void dbToBaseData(String code, String tablename, String orderby) {
		dbToBaseData(code, "select A.id as value,A.* from "
				+ tablename + " A order by " + orderby);
	}
	public static void dbToBaseDataForDb2(String code,String database,String sql,Object...params){
		List<Record> list = Db.use(database).find(sql,params);
		JSONObject basedata = new JSONObject();
		for (Record record : list) {
			JSONObject item = JSONObject.parseObject(record.toJson());
			item.put("value", record.get("VALUE").toString());
			item.put("name", record.get("NAME").toString());
			if(record.get("ENABLE_FLAG")!=null){
				item.put("enable_flag", record.get("ENABLE_FLAG").toString());
			}else {
				item.put("enable_flag", "T");
			}
			basedata.put(item.getString("value"), item);
		}
		Cache cache = Redis.use();
		cache.hset(Constant.R_BASEDATA, code, basedata);
		System.out.println("业务数据字典："+code);
	}
	public static void dbToBaseData(String code, String sql) {
		List<Record> list = Db.find(sql);
		JSONObject basedata = new JSONObject();
		for (Record record : list) {
			basedata.put(record.get("value").toString(),JSONObject.parseObject(record.toJson()));
		}
		Cache cache = Redis.use();
		cache.hset(Constant.R_BASEDATA, code, basedata);
		logger.info("业务数据字典：" + code);
	}
	public static void dbUpdateBaseData(String code, String tablename, Integer id) {
		Record record = Db.findById(tablename, id);
		record.set("value", record.getInt("id").toString());
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if(basedata==null){
			basedata = new JSONObject();
		}
		basedata.put(record.get("value").toString(),JSONObject.parseObject(record.toJson()));
		cache.hset(Constant.R_BASEDATA, code, basedata);
	}
	public static void dbUpdateBaseData(String code, Model<? extends Model<?>> model) {
		Record record = model.toRecord();
		record.set("value", record.getInt("id").toString());
		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if(basedata==null){
			basedata = new JSONObject();
		}
		basedata.put(record.get("value").toString(),JSONObject.parseObject(record.toJson()));
		cache.hset(Constant.R_BASEDATA, code, basedata);
	}
	public static void dbRemoveBaseData(String code,Integer id){

		Cache cache = Redis.use();
		JSONObject basedata = cache.hget(Constant.R_BASEDATA, code);
		if(basedata != null ){
			basedata.remove(id.toString());
		}
		cache.hset(Constant.R_BASEDATA, code, basedata);
	}

	/***
	 * 解析权限
	 * 
	 * @param config
	 */
	public void parseAuth(JSONObject config) {
		if (config == null || config.get(c_auth) == null)
			return;
		JSONObject authObject = config.getJSONObject(c_auth);
		Cache cache = Redis.use();
		for (String portal : authObject.keySet()) {
			JSONArray item = authObject.getJSONArray(portal);
			for (int i = 0; i < item.size(); i++) {
				Map<String, JSONObject> map = cache.hget(Constant.R_AUTH,portal);
				if (map == null) map = new LinkedHashMap<>();
				item.getJSONObject(i).put("path", Jsub.path);
				map.put(item.getJSONObject(i).getString("rights"),item.getJSONObject(i));
				cache.hset(Constant.R_AUTH, portal, map);
				logger.info(portal+"权限缓存："+ item.getJSONObject(i).getString("rights"));
			}
		}
	}

	/***
	 * 解析数据表配置
	 * 
	 * @param config
	 * @return Map<String,TableBean>
	 */
	public Map<String, TableBean> parseTable(JSONObject config) {
		Map<String, TableBean> tableMap = new HashMap<String, TableBean>();
		if (config == null || config.get(c_table) == null) return tableMap;
		for (String tableName : config.getJSONObject(c_table).keySet()) {
			TableBean tableBean = new TableBean();
			tableBean.setTableName(tableName);

			Map<String, ColumnBean> columnMap = new LinkedHashMap<>();
			JSONObject tableObject = config.getJSONObject(c_table).getJSONObject(tableName);
			for (String columnName : tableObject.keySet()) {
				String[] columnConfig = tableObject.getObject(columnName,String[].class);
				ColumnBean columnBean = new ColumnBean();
				columnBean.setName(columnName);
				columnBean.setType(columnConfig[0]);
				if (columnConfig.length > 1) {
					columnBean.setDefaultValue(columnConfig[1]);
				}
				columnMap.put(columnName, columnBean);
			}
			tableBean.setColumnMap(columnMap);
			tableMap.put(tableName, tableBean);
		}
		return tableMap;
	}
	/***
	 * 初始化事件
	 * @param me
	 * @param app_config
	 * @param server_config
	 */
	public void parseEvent(Plugins me,JSONObject app_config,
			JSONObject server_config){
		if(app_config==null||app_config.get(c_event)==null) return;
		JSONObject eventConfig = app_config.getJSONObject(c_event);
		if (server_config != null && server_config.get(c_event) != null) {
			eventConfig = server_config.getJSONObject(c_event);
		}
		me.add(new EventPlugin(true,eventConfig.getString("package"),Constant.R_EVENT).async()
				.setRmiServer(eventConfig.getString("host"),Jsub.folder));
	}
	/***
	 * 解析Redis
	 * 
	 * @param me
	 * @param app_config
	 * @param server_config
	 */
	public void parseRedis(Plugins me, JSONObject app_config,
			JSONObject server_config) {
		if (app_config == null)
			return;
		JSONObject redisConfig = app_config.getJSONObject(c_redis);
		if (redisConfig == null)
			return;
		if (server_config != null && server_config.get(c_redis) != null) {
			redisConfig = server_config.getJSONObject(c_redis);
		}

		String pre = redisConfig.getString("pre");
		me.add(new RedisPlugin(pre + "CACHE", redisConfig.getString("host")));
		Constant.R_PRE = pre;
		Constant.R_PARAM = pre + Constant.R_PARAM;
		Constant.R_BASEDATA = pre + Constant.R_BASEDATA;
		Constant.R_AUTH = pre + Constant.R_AUTH;
		Constant.R_TEMPLATE = pre + Constant.R_TEMPLATE;
		Constant.R_USER = pre + Constant.R_USER;
		Constant.R_EVENT = pre + Constant.R_EVENT;
		Constant.R_EXPORT = pre + Constant.R_EXPORT;
		Constant.R_TREE = pre + Constant.R_TREE;
		Constant.R_FLOW = pre + Constant.R_FLOW;
	}
	
	public void parseWeixin(Plugins me,JSONObject app_config,JSONObject server_config){
		if (app_config == null)
			return;
		JSONObject weixinConfig = app_config.getJSONObject(c_weixin);
		if (weixinConfig == null)
			return;
		if (server_config != null && server_config.get(c_weixin) != null) {
			weixinConfig = server_config.getJSONObject(c_weixin);
		}
		for(String name:weixinConfig.keySet()){
			JSONObject item = weixinConfig.getJSONObject(name);
			me.add(new WeixinPlugin(name,item.getString("appid"),item.getString("serect"),item.getString("token")));
		}
	}
	/***
	 * 解析常量
	 * 
	 * @param app_config
	 * @param server_config
	 */
	public void parseConstant(JSONObject app_config, JSONObject server_config) {
		if (app_config != null && app_config.get("dev") != null) {
			Jsub.dev = app_config.getBooleanValue("dev");
		}
		if (app_config != null && app_config.get("yun") != null) {
			Jsub.yun = app_config.getBooleanValue("yun");
		}
		// 应用路径
		Jsub.path = "/" + Jsub.folder;
		//默认资源文件是本应用
		Jsub.res_path = Jsub.path;
		// 默认上传文件上传到tomcat服务器webapp文件夹下，结尾不带斜杠
		Jsub.upload_path = PathUtil.getWebPath();
		Jsub.upload_folder = "uploadfiles";
		
		if (app_config.get("res_path") != null)
			Jsub.res_path = app_config.getString("res_path");
		if (app_config.get("upload_path") != null)
			Jsub.upload_path = app_config.getString("upload_path");
		if (app_config.get("upload_folder") != null)
			Jsub.upload_folder = app_config.getString("upload_folder");

		if (server_config != null) {
			// 场景：服务器端变更为开发模式
			if (server_config.get("dev") != null)
				Jsub.dev = server_config.getBooleanValue("dev");
			if (server_config.get("yun") != null)
				Jsub.yun = server_config.getBooleanValue("yun");
			// 场景：服务器端nginx修改项目路径
			if (server_config.get("path") != null)
				Jsub.path = server_config.getString("path");
			// 场景：服务器端修改资源应用路径
			if (server_config.get("res_path") != null)
				Jsub.res_path = server_config.getString("res_path");
			
			// 场景：服务器端修改上传文件保存路径。记得加个nginx帮助下载。
			if (server_config.get("upload_path") != null)
				Jsub.upload_path = server_config.getString("upload_path");
			if (server_config.get("upload_folder") != null)
				Jsub.upload_folder = server_config.getString("upload_folder");
		}
	}

	/***
	 * 解析数据库配置
	 * 
	 * @param me
	 * @param app_config
	 * @param server_config
	 * @return Map<String,Database>
	 */
	public Map<String, Database> parseDb(Plugins me, JSONObject app_config,
			JSONObject server_config) {
		Map<String, Database> dbMap = new LinkedHashMap<>();
		if (app_config == null || app_config.get(c_db) == null) return dbMap;

		JSONObject dbConfig = app_config.getJSONObject(c_db);
		if (dbConfig == null) return dbMap;

		// 服务器配置可以覆盖应用配置
		if (server_config != null && server_config.get(c_db) != null) {
			dbConfig = server_config.getJSONObject(c_db);
		}
		for (String key : dbConfig.keySet()) {
			JSONObject db = dbConfig.getJSONObject(key);
			ActiveRecordPlugin arp = null;
			try {
				switch (db.getString("type")) {
				case mysql:
					logger.info("jdbc:mysql://"
							+ db.getString("host") + ":" + db.getString("port")
							+ "/" + db.getString("dbname")
							+ "?useUnicode=true&characterEncoding=utf-8");
					logger.info(db.getString("username")+" "+db.getString("password"));
					C3p0Plugin c3p0Plugin = new C3p0Plugin("jdbc:mysql://"
							+ db.getString("host") + ":" + db.getString("port")
							+ "/" + db.getString("dbname")
							+ "?useUnicode=true&characterEncoding=utf-8",
							db.getString("username"), db.getString("password"));
					me.add(c3p0Plugin);
					c3p0Plugin.start();

					logger.info(db.getString("dbname")+"连接成功！");
					arp = new ActiveRecordPlugin(key, c3p0Plugin);
					if (Jsub.dev) arp.setShowSql(true);
					dbMap.put(key,new Database(key, db.getString("type"), 
							c3p0Plugin.getDataSource(), arp, 
							db.getString("dbname")));
					break;
				case jndi:
					DataSource datasource = (DataSource) new InitialContext()
							.lookup("java:/comp/env/" + db.getString("dbname"));
					arp = new ActiveRecordPlugin(key, datasource);
					arp.setDialect(new AnsiSqlDialect());
					if (Jsub.dev) arp.setShowSql(true);
					dbMap.put(key, new Database(key, db.getString("type"),
							datasource, arp, db.getString("dbname")));
					break;
				default:
					break;
				}
			} catch (Exception e) {
				e.printStackTrace();
				logger.error(key+"数据源连接失败！！！！！！！！！");
				logger.error(key+"数据源连接失败！！！！！！！！！");
				logger.error(key+"数据源连接失败！！！！！！！！！");
			}
			arp.setCache(new MyRedisCache());
			me.add(arp);
		}
		logger.info("检查到" + dbMap.size() + "个数据源");
		return dbMap;
	}
}
